package q1.project.core.exception;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.Arrays;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import q1.project.core.result.Result;

/**
 * 全局校验异常处理器 统一处理 Bean Validation 注解的校验异常
 *
 * @author Q1 Team
 * @version 1.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalValidationExceptionHandler {

  /** 处理 @Valid 注解对 @RequestBody 参数校验失败的异常 */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
    String message =
        ex.getBindingResult().getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining("; "));

    log.warn("参数校验失败: {}", message);
    return Result.error(HttpStatus.BAD_REQUEST.value(), message);
  }

  /** 处理 @Validated 注解对 @PathVariable 和 @RequestParam 参数校验失败的异常 */
  @ExceptionHandler(ConstraintViolationException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleConstraintViolation(ConstraintViolationException ex) {
    String message =
        ex.getConstraintViolations().stream()
            .map(ConstraintViolation::getMessage)
            .collect(Collectors.joining("; "));

    log.warn("参数校验失败: {}", message);
    return Result.error(HttpStatus.BAD_REQUEST.value(), message);
  }

  /** 处理表单绑定异常（@ModelAttribute 校验失败） */
  @ExceptionHandler(BindException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleBindException(BindException ex) {
    String message =
        ex.getBindingResult().getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining("; "));

    log.warn("表单参数校验失败: {}", message);
    return Result.error(HttpStatus.BAD_REQUEST.value(), message);
  }

  /** 处理非法参数异常 */
  @ExceptionHandler(IllegalArgumentException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleIllegalArgument(IllegalArgumentException ex) {
    log.warn("非法参数: {}", ex.getMessage());
    return Result.error(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
  }

  /** 处理非法状态异常 */
  @ExceptionHandler(IllegalStateException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleIllegalState(IllegalStateException ex) {
    log.warn("非法状态: {}", ex.getMessage());
    return Result.error(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
  }

  /** 处理枚举类型转换失败异常 当前端传入非法的枚举值时触发 */
  @ExceptionHandler(MethodArgumentTypeMismatchException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public Result<Void> handleEnumConversion(MethodArgumentTypeMismatchException ex) {
    String message = String.format("参数 '%s' 的值 '%s' 不合法", ex.getName(), ex.getValue());

    // 如果是枚举类型，列出所有可用值
    if (ex.getRequiredType() != null && ex.getRequiredType().isEnum()) {
      Object[] enumValues = ex.getRequiredType().getEnumConstants();
      String validValues =
          Arrays.stream(enumValues).map(Object::toString).collect(Collectors.joining(", "));
      message += "，可选值：" + validValues;
    }

    log.warn("枚举转换失败: {}", message);
    return Result.error(HttpStatus.BAD_REQUEST.value(), message);
  }
}
